#include "interboursier.h"

void launch_interboursier(module* mdl, const int port, const int nb_serv) {
	server srv = *(mdl->srv);
	strcpy(srv.name, mdl->name);
	srv.my_port=port;
	srv.isAlive = 1;
	if ((srv.sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		perror("socket");
		exit(1);
	}
	srv.local_addr.sin_family = AF_INET; /* host byte order */
	srv.local_addr.sin_port = htons(port); /* short, network byte order */
	srv.local_addr.sin_addr.s_addr = INADDR_ANY; /* auto-remplissage avec mon IP */
	bzero(&(srv.local_addr.sin_zero), 8); /* zero pour le reste de struct */

	if (bind(srv.sockfd,( struct sockaddr *)&(srv.local_addr),sizeof(struct sockaddr))== -1) {
		perror("bind");
		exit(1);
	}

	if (listen(srv.sockfd, 5) == -1) { // on choisit un nombre max de connexions = 5
		perror("listen");
		exit(1);
	}
	char tmp[1024] = {0};
	sprintf(tmp,"créé.");
	log_srv(&srv,tmp);
	if(!fork()) { /* fils première génération - permet de revenir de la méthode par contre créé des zombies */
		while(1) { // boucle d' accept() pour recevoir les connexions
			srv.sin_size = sizeof(struct sockaddr_in);
			int new_fd; // descripteur lié au socket de la connexion courante
			if ((new_fd = accept(srv.sockfd, (struct sockaddr *)&(srv.remote_addr),
							&(srv.sin_size))) == -1) {
				perror("accept");
				continue;
			}
			if(!srv.isAlive) {
				close(new_fd); //si une connexion arrive alors que le programme est terminé, on sort
				break; return;
			}
			sprintf(tmp, "connexion entrante depuis %s:%d",inet_ntoa(srv.remote_addr.sin_addr),ntohs(srv.remote_addr.sin_port));
			log_srv(&srv,tmp);
			id2port* routing_table =( id2port*) malloc(sizeof(id2port));
			int i;
			for(i=0;i<nb_serv;i++)	add_route(routing_table,i,PORT_INTERBOURSIER+(i*2)+1);
			if (!fork()) { /* fils seconde génération - permet d'être multiclient */
				if(!srv.isAlive) {
					close(new_fd); //si une connexion arrive alors que le programme est terminé, on sort
					return;
				}
				char answer[MAXRECVDATA] = {0};

				srv.recvdata = (msg*) malloc (sizeof (msg));
				char recvdata[MAXRECVDATA] = {0};
				bzero(srv.recvdata,sizeof (msg));
				if ((srv.numbytes=recv(new_fd, recvdata, MAXRECVDATA, 0)) == -1) {
					perror("recv");
					log_srv(&srv,"erreur lors du recv!");
					exit(1);
				}

				if(strlen(recvdata)==9) { // alors c'est un message d'identification de la forme: 0001:1201
					char tmp1[1024] = {0}, tmp2[1024] = {0};
					strncpy(tmp1,recvdata,4);
					strncpy(tmp2,strstr(recvdata,":")+1,4);
					//add_route(routing_table,atoi(tmp1),atoi(tmp2));
					int daport = route(routing_table,atoi(tmp1));
					sprintf(answer,"Confirmation d'autentification: bourse %d port %d",atoi(tmp1),daport);
					if (send(new_fd, answer, strlen(answer), 0) == -1)
					perror("Erreur lors de l'envoi de la confirmation d'autentification du serveur d'acquisition");
					else log_srv(&srv,answer);
					close(new_fd);
					exit(1); // on sort juste après le traitement
				}
				srv.recvdata = str_to_msg(recvdata);
				if(srv.recvdata==NULL) {
					log_srv(&srv,"réception d'un message non valide");
					close(new_fd);
				}
				sprintf(tmp,"%d octets reçu",srv.numbytes);
				log_msg_srv(&srv,srv.recvdata,tmp);

				char idbourse[5] = {0}; // identifiant de la bourse

				strncpy(idbourse,srv.recvdata->action,4); // on copie l'id de la bourse du message
				int remote_port = route(routing_table,atoi(idbourse));
				if(remote_port==-1) {
					sprintf(answer,"Erreur: la bourse %d n'est pas en ligne!",atoi(idbourse));
					log_srv(&srv,answer);
					char tmptype[11] = {0}; sprintf(tmptype,"Accuse%s",srv.recvdata->type);
					msg* answered = create_msg(srv.recvdata->emetteur,tmptype,srv.recvdata->action,"0");
					sprintf(answer,"%s",msg_to_str(answered));
					if (send(new_fd, answer, strlen(answer), 0) == -1)
					perror("Erreur lors de l'envoi de l'erreur concernant la bourse non connectée");
					close(new_fd);
					exit(1);
				}

				int ack_sockfd;
				struct sockaddr_in exec_addr; /* adresse du serveur d'exécution */
				struct hostent *he;

				if ((he=gethostbyname("localhost")) == NULL) {
					herror("gethostbyname");
					exit(1);
				}

				if ((ack_sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
					perror("socket");
					exit(1);
				}

				exec_addr.sin_family = AF_INET; /* host byte order */
				exec_addr.sin_port = htons(remote_port); /* short, network byte order */
				exec_addr.sin_addr = *((struct in_addr *)he->h_addr);
				bzero(&(exec_addr.sin_zero), 8); /* zero pour le reste de struct */
				log_smth("envoi des données vers le serveur d'acquisition sur %s:%d",inet_ntoa(exec_addr.sin_addr),ntohs(exec_addr.sin_port));

				if (connect(ack_sockfd,( struct sockaddr *)&(exec_addr), sizeof(struct sockaddr)) == -1) {
					sprintf(tmp,"%s erreur de connexion",srv.name);
					perror(tmp);
					log_srv(&srv,"déjà connecté ou impossible de se connecter au serveur d'acquisition");
					exit(1);
				}

				if (send(ack_sockfd, recvdata, strlen(recvdata), 0) == -1) {
					sprintf(tmp,"%s erreur d'envoi de message vers le serveur d'acquisition",srv.name);
					perror(tmp);
					log_srv(&srv, "erreur lors de l'envoi du message vers le serveur d'acquisition");
					exit(1);
				}
				// on attend désormais la réponse du serveur d'exécution
				bzero(srv.recvdata,sizeof (msg)); srv.numbytes=0;
				if (srv.isAlive && (srv.numbytes=recv(ack_sockfd, recvdata, MAXRECVDATA, 0)) == -1) {
					perror("recv");
					log_srv(&srv,"erreur lors du recv!");
					sprintf(tmp,"Erreur lors de la réception de la confirmation du serveur d'acquisition. Au revoir.\n");
					send(new_fd, tmp, strlen(tmp), 0);
					close(new_fd);
					exit(1);
				}
				srv.recvdata = str_to_msg(recvdata);
				sprintf(tmp,"%d octets reçu par %s %s:%d",srv.numbytes,srv.name, inet_ntoa(srv.remote_addr.sin_addr),ntohs(srv.remote_addr.sin_port));
				log_msg(tmp,srv.recvdata);
				close(ack_sockfd); // on ferme la connexion avec le serveur d'exécution
				// puis on renvoit cette réponse au terminal de base
				if (send(new_fd, recvdata, strlen(recvdata), 0) == -1) {
					sprintf(tmp,"%s erreur d'envoi du message vers le serveur d'acquisition",srv.name);
					perror(tmp);
					log_srv(&srv, "erreur lors de l'envoi du message vers le serveur d'acquisition");
					exit(1);
				}

				close(new_fd); // on ferme la connexion une fois le message reçu et renvoyé
			}
			while(waitpid(-1,NULL,WNOHANG) > 0); // nettoyage des processus fils
			close(new_fd);

		} // fin while du "accept"
	} // fin fils première génération


}

/**
 * Permet de récupérer un socket descriptor à partir de l'identifiant d'une bourse
 */
int route(id2port* tbl, int id) {
	int rtn_port=-1;
	int i=0;
	for (i=0; i<NB_BOURSES; i++) {
		//printf("i=%d \t tbl->id = %d\n",i,tbl->id_bourse[i]);
		if (tbl->id_bourse[i]==id) {
			rtn_port=tbl->port_bourse[i];
			break;
		}
	}
	return rtn_port;
}

int add_route(id2port* tbl, int id, int port) {
	static int position=0; // position dans le tableau
	if (position > NB_BOURSES)
		return 0; // plus de place
	if (route(tbl, id)!=-1)
		return 1; // le serveur est déjà autentifié
	tbl->id_bourse[position]=id;
	tbl->port_bourse[position]=port;
	position+=1;
	return 1;
}
